/***************************************************************************
 *   Copyright (C) 2009 by Dead_Body                                       *
 *   jamesholodnak@gmail.com                                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "defines.h"
#include "pce/pce.h"

#define MAWR pce->vdc.regs[0]
#define MARR pce->vdc.regs[1]
#define VRR pce->vdc.regs[2]
#define VWR pce->vdc.regs[2]
#define CR pce->vdc.regs[5]
#define RCR pce->vdc.regs[6]
#define BXR pce->vdc.regs[7]
#define BYR pce->vdc.regs[8]
#define MWR pce->vdc.regs[9]
#define HSR pce->vdc.regs[10]
#define HDR pce->vdc.regs[11]
#define SATB pce->vdc.regs[19]

static u8 reg;

void pce_vdc_reset()
{
	pce->vdc.status = 0;
	pce->scanline = 0;
	pce->cycles = 0;
	pce->frames = 0;
}

u8 pce_vdc_read(u32 address)
{
	u8 ret = 0;

	switch(address & 3) {
		/*
		 * status register (from pce docs)
		 * 
		 * [bit 7] always zero
		 * [bit 6] 'BSY' flag '1' when a DMA transfer is happening (although this is not completely clear at this time).
		 * [bit 5] 'VD' flag '1' when Vertical Sync happens, otherwise a '0' (uncertain)
		 * [bit 4] 'DV' flag (unknown)
		 * [bit 3] 'DS' flag (unknown)
		 * [bit 2] 'RR' flag '1' when RCR-position reached.
		 * [bit 1] 'OR' flag (unknown)
		 * [bit 0] 'CR' flag (unknown) 
		 */
		case 0:
			return(pce->vdc.status);

		/*
		 * low byte of data register
		 */
		case 2:
			return(ret);

		/*
		 * high byte of data register
		 */
		case 3:
			switch(reg) {
				case 2:
					switch((pce->vdc.regs[R_CR] >> 11) & 3) {
						case 0: pce->vdc.regs[R_MARR] += 1; break;
						case 1: pce->vdc.regs[R_MARR] += 32; break;
						case 2: pce->vdc.regs[R_MARR] += 64; break;
						case 3: pce->vdc.regs[R_MARR] += 128; break;						
					}
					break;
			}
			return(ret);
	}
	return(0);
}

void pce_vdc_write(u32 address,u8 data)
{
	switch(address & 3) {
		/*
		 * register select
		 * 
		 * 0	MAWR	Memory Address Write Register
		 * 1	MARR	Memory Address Read Register
		 * 2	VRR		VRAM Read Register
		 * 2	VWR		VRAM Write Register
		 * 5	CR		Control Register
		 * 6	RCR		Raster Counter Register
		 * 7	BXR		Background X-Scroll Register
		 * 8	BYR		Background Y-Scroll Register
		 * 9	MWR		Memory-access Width Register
		 * 10	HSR		Horizontal Sync Register (?)
		 * 11	HDR		Horizontal Display Register (?)
		 * 12	VPR		(unknown)
		 * 13	VDW		(unknown use)
		 * 14	VCR		(unknown use)
		 * 15	DCR		(DMA) Control Register
		 * 16	SOUR	(DMA) Source Register
		 * 17	DESR	(DMA) Destination Register
		 * 18	LENR	(DMA) Length Register
		 * 19	SATB	Sprite Attribute Table
		 */
		case 0:
			reg = data;
			break;
		case 2:
			log_message("reg lo write, %d = $%02X\n",reg,data);
			pce->vdc.regs[reg] &= 0xFF00;
			pce->vdc.regs[reg] |= data;
			break;
		case 3:
			log_message("reg hi write, %d = $%02X\n",reg,data);
			pce->vdc.regs[reg] &= 0x00FF;
			pce->vdc.regs[reg] |= data << 8;
			switch(reg) {
				case 2:
					log_message("vram write, MAWR = $%04X ( = $%04X)\n",MAWR,VWR);
					pce->vdc.vram[MAWR] = VWR;
					switch((pce->vdc.regs[R_CR] >> 11) & 3) {
						case 0: pce->vdc.regs[R_MAWR] += 1; break;
						case 1: pce->vdc.regs[R_MAWR] += 32; break;
						case 2: pce->vdc.regs[R_MAWR] += 64; break;
						case 3: pce->vdc.regs[R_MAWR] += 128; break;						
					}
					break;
			}
			break;
	}
}

int map_width_table[4] = {32,64,128,128};
int map_height_table[2] = {32,64};

void pce_vdc_drawtileline(u16 *dest,int tileidx,int line)
{
	//four bpp
	u8 b0,b1,b2,b3;
	u8 *vramptr = (u8*)pce->vdc.vram;
	
	vramptr += (tileidx & 0xFFF) * 32;
	vramptr += line * 2;
	b0 = *(vramptr + 0);
	b1 = *(vramptr + 1);
	b2 = *(vramptr + 16);
	b3 = *(vramptr + 17);
	*dest++ = ((b0 >> 7) & 1) | ((b1 >> 6) & 2) | ((b2 >> 5) & 4) | ((b3 >> 4) & 8);
	*dest++ = ((b0 >> 6) & 1) | ((b1 >> 5) & 2) | ((b2 >> 4) & 4) | ((b3 >> 3) & 8);
	*dest++ = ((b0 >> 5) & 1) | ((b1 >> 4) & 2) | ((b2 >> 3) & 4) | ((b3 >> 2) & 8);
	*dest++ = ((b0 >> 4) & 1) | ((b1 >> 3) & 2) | ((b2 >> 2) & 4) | ((b3 >> 1) & 8);
	*dest++ = ((b0 >> 3) & 1) | ((b1 >> 2) & 2) | ((b2 >> 1) & 4) | ((b3 >> 0) & 8);
	*dest++ = ((b0 >> 2) & 1) | ((b1 >> 1) & 2) | ((b2 >> 0) & 4) | ((b3 << 1) & 8);
	*dest++ = ((b0 >> 1) & 1) | ((b1 >> 0) & 2) | ((b2 << 1) & 4) | ((b3 << 2) & 8);
	*dest++ = ((b0 >> 0) & 1) | ((b1 << 1) & 2) | ((b2 << 2) & 4) | ((b3 << 3) & 8);
}

/*
 * pce_vdc_drawline(u16 *dest)
 * 
 * draws the current scanline to dest.  ensure dest is at
 * least (512 + 16*2) bytes big.  offset to line data begins
 * 16 bytes into dest.  line width is determined by HDR reg.
 * 
 */
void pce_vdc_drawline(u16 *dest)
{
	int t,x,y;
	int map_width,map_height;		//tilemap width/height
	
	//determine the tilemap width and height
	map_width = map_width_table[(MWR >> 4) & 3];
	map_height = map_height_table[(MWR >> 6) & 1];
	
	x = 0;
	y = 0;
	
	//draw appropriate number of tiles
	for(t=0;t<=(HDR & 0x3F);t++) {
		u16 tileinfo;
		
		tileinfo = pce->vdc.vram[t + (pce->scanline - 21) * map_width];
		pce_vdc_drawtileline(dest + 16 + (t * 8),tileinfo,(pce->scanline - 21) & 7);
	}
}

/*
 * pce_vdc_line()
 * 
 * advance the vdc one line
 */
void pce_vdc_line()
{
	static u16 screenline[512 + 64];

	//vblank 'scanlines'
	if(pce->scanline == 0) {
		//set vsync flag
		pce->vdc.status |= 0x20;
		//clear rcr flag
		pce->vdc.status &= ~4;
	}
	else if(pce->scanline == 20) {
		//clear vsync flag
		pce->vdc.status &= ~0x20;
	}
	else if(pce->scanline < 20) {
	}
	
	//last scanline
	else if(pce->scanline == 261) {
	}

	//screen scanlines
	else {
		//draw the line and send off the line to the video output system
		pce_vdc_drawline(screenline);
		video_updateline16(pce->scanline - 21,screenline + 16);
//		log_message("draw line %d\n",pce->scanline - 21);
	}

	//raster line hit
	if((RCR - 64) == (pce->scanline - 21)) {
		if(CR & 4) {
			hu6280_setirq(CPU_IRQ1);
			pce->vdc.status |= 4;
		}
	}

	//increment scanline counter
	pce->scanline++;
	if(pce->scanline >= 262) {
		pce->scanline = 0;
		video_endframe();
		sound_update();
	}
}
